package weeny.pre.renderer;

import java.awt.Point;
import java.awt.geom.CubicCurve2D;
import java.awt.geom.GeneralPath;
import java.awt.geom.Line2D;
import java.awt.geom.PathIterator;
import java.awt.geom.Point2D;

import prefuse.Display;
import prefuse.render.PolygonRenderer;
import prefuse.util.ColorLib;
import prefuse.visual.VisualItem;
import weeny.util.BezierUtil;


/**
 * There is a problem about PolygonRenderer that even you only draw the outline
 * of the object and make its internal part transparent, the total shape still
 * block everything underneath it. So you can see it through but you cannot
 * drag things under it.
 * 
 * This class makes the polyline object can be draged only on its line shape.
 * By override the locatePoint(...) function, we make that the visual item only
 * responses to the point inside its contour when it is filled with 
 * non-transparent color AND use filling renderer type
 * 
 * @author weeny
 */
public class PolylineRenderer extends PolygonRenderer{
	
	public PolylineRenderer() {
		super();
	}
	public PolylineRenderer(int type){
		super(type);
	}
	
	private Line2D line = new Line2D.Double();
	private CubicCurve2D cubicC = new CubicCurve2D.Double();
	private double[] coords = new double[8];
	@Override
	public boolean locatePoint(Point2D p, VisualItem item) {
		int renderType = getRenderType(item);
		double halfWidth = item.getStroke().getLineWidth()/2;
		int filColor = item.getFillColor();
		if (renderType == RENDER_TYPE_DRAW || ColorLib.alpha(filColor) == 0) {
			if (!item.getBounds().contains(p)) {
				return false;
			}
			//a shortcut, if the it is complete opaque, we just check the color
			//first if the color does not match, return false, otherwise, go on
			//to check with other things
			if (ColorLib.alpha(item.getStrokeColor())==255) {
				Display display = item.getVisualization().getDisplay(0);
				Point scr = new Point();
				display.getTransform().transform(p, scr);
				if(display.getOffscreenBuffer()==null){
					display.paintComponent(DEFAULT_GRAPHICS);
				}
				int color = display.getOffscreenBuffer().getRGB(scr.x, scr.y);
				//if color does not match, it means that it is not on the curve
				//or polyline but if it does match, there is not sure that it 
				//is on the curve or polyline because it is maybe some other 
				//item happen to have the same color
				if (color != item.getStrokeColor()) {
					return false;
				}
			}
			GeneralPath m_path = (GeneralPath) getShape(item);
	        PathIterator pathIt = m_path.getPathIterator(null);
	        double[] segs = new double[6];
	        coords[0] = 0;
	        coords[1] = 0;
	        Point2D tmpPnt = new Point2D.Double();
	        while(!pathIt.isDone()){
	        	int code = pathIt.currentSegment(segs);
	        	if (code == PathIterator.SEG_CUBICTO) {
	        		System.arraycopy(segs, 0, coords, 2, 6);
	        		cubicC.setCurve(coords, 0);
					double tmp = BezierUtil.onCurve(cubicC, p, tmpPnt);
					if (tmp < halfWidth) {
						return true;
					}
					System.arraycopy(segs, 4, coords, 0, 2);
				}
	        	else if (code == PathIterator.SEG_MOVETO){
	        		System.arraycopy(segs, 0, coords, 0, 2);
	        	}
	        	else if (code == PathIterator.SEG_LINETO){
	        		line.setLine(coords[0], coords[1], segs[0], segs[1]);
	        		boolean tmp = line.intersects(
	        				p.getX()-halfWidth,p.getY()-halfWidth,
	        				halfWidth*2,halfWidth*2);
	        		if (tmp) {
						return true;
					}
	        		System.arraycopy(segs, 0, coords, 0, 2);
	        	}
	        	else {
	        		throw new IllegalStateException("not support quad curve");
	        	}
	        	pathIt.next();
	        }
			return false;
		}
		else{
			return super.locatePoint(p, item);	
		}
	}
}
